package cn.xzqwjw.taskmanager.service.impl;

import cn.xzqwjw.taskmanager.common.customEnum.ResponseCodeEnum;
import cn.xzqwjw.taskmanager.common.exception.CustomException;
import cn.xzqwjw.taskmanager.domain.pojo.SysAdmin;
import cn.xzqwjw.taskmanager.domain.pojo.SysFrontRoute;
import cn.xzqwjw.taskmanager.domain.pojo.SysPurview;
import cn.xzqwjw.taskmanager.domain.pojo.SysRole;
import cn.xzqwjw.taskmanager.domain.vo.ResponseVo;
import cn.xzqwjw.taskmanager.repository.SysAdminMapper;
import cn.xzqwjw.taskmanager.service.SysAdminService;
import cn.xzqwjw.taskmanager.service.SysFrontRouteService;
import cn.xzqwjw.taskmanager.service.SysPurviewService;
import cn.xzqwjw.taskmanager.service.CategoryService;
import cn.xzqwjw.taskmanager.utils.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @author wjw
 */
@Service("SysAdminService")
@Transactional(rollbackFor = Exception.class)
public class SysAdminServiceImpl extends ServiceImpl<SysAdminMapper, SysAdmin>
    implements SysAdminService {

  private static final String ROLE_NAME_SUPERADMIN = "ROLE_SUPERADMIN";
  private static final int ID_SUPERADMIN = 9;

  private final SysAdminMapper adminMapper;
  private final SysFrontRouteService frontRouteService;
  private final SysPurviewService purviewService;
  private final CategoryService categoryService;
  private final PasswordEncoder passwordEncoder;

  @Autowired
  public SysAdminServiceImpl(
      SysAdminMapper adminMapper,
      SysPurviewService purviewService,
      SysFrontRouteService frontRouteService,
      CategoryService categoryService) {
    this.adminMapper = adminMapper;
    this.purviewService = purviewService;
    this.frontRouteService = frontRouteService;
    this.categoryService = categoryService;
    // 默认使用 bcrypt， strength=10
    // this.passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
    this.passwordEncoder = CryptoUtil.initEncoder();
  }

  @Override
  public String add(SysAdmin admin) {
    if (isAdded(admin.getUsername())) {
      throw new CustomException(ResponseCodeEnum.UNPROCESSABLE_ENTITY);
    }
    PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    admin.setPassword(passwordEncoder.encode(admin.getPassword()));
    if (save(admin)) {
      return JsonUtil.obj2Json(ResponseVo.success(ResponseCodeEnum.OK_CREATED));
    } else {
      throw new CustomException(ResponseCodeEnum.ERROR_INSERT);
    }
  }

  @Override
  public String delById(Integer id) {
    // 不能删除超级管理员 rush，他的id是9
    if (id == ID_SUPERADMIN) {
      throw new CustomException(ResponseCodeEnum.ERROR_DELETE);
    }
    if (removeById(id)) {
      return JsonUtil.obj2Json(ResponseVo.success(ResponseCodeEnum.OK_DELETED));
    } else {
      throw new CustomException(ResponseCodeEnum.ERROR_DELETE);
    }
  }

  @Override
  public String modifyAll(SysAdmin admin) {
    // 不能修改超级管理员 rush，他的id是9
    if (admin.getId() == ID_SUPERADMIN) {
      throw new CustomException(ResponseCodeEnum.ERROR_UPDATE);
    }
    if (updateById(admin)) {
      return JsonUtil.obj2Json(ResponseVo.success(ResponseCodeEnum.OK_UPDATED));
    } else {
      throw new CustomException(ResponseCodeEnum.ERROR_UPDATE);
    }
  }

  /**
   * 修改管理员是否启用、账号是否过期、账号是否锁定、密码是否过期
   * 1、is_enabled 账号是否启用
   * 2、is_account_non_expired 账号是否未过期
   * 3、is_account_non_locked 账号是否未锁定
   * 4、is_credentials_non_expired 密码是否未过期
   */
  @Override
  public String patchStatus(Integer id, String action) {
    switch (action) {
      case "Enabled":
        adminMapper.patchEnabled(id);
        break;
      case "AccountExpired":
        adminMapper.patchAccountExpired(id);
        break;
      case "AccountLocked":
        adminMapper.patchAccountLocked(id);
        break;
      case "CredentialsExpired":
        adminMapper.patchCredentialsExpired(id);
        break;
      default:
        throw new CustomException(ResponseCodeEnum.BAD_REQUEST);
    }
    return JsonUtil.obj2Json(ResponseVo.success(ResponseCodeEnum.OK_PATCHED));
  }

  /**
   * 修改密码有四种情况，这里暂时只做了管理员修改自己密码的情况
   * 1、管理员自己修改密码，type="own",passwordOld,passwordNew
   * 2、管理员忘记密码，超级管理员修改密码，type="super",passwordSuper,passwordNew
   * 3、管理员忘记密码，通过手机重置密码，type="reset-phone",phoneNumber
   * 4、管理员忘记密码，通过电子邮件重置密码，type="reset-email",email
   */
  @Override
  public String patchPassword(Integer id, String paramsJson) {
    CommonUtil.checkId(id);
    CommonUtil.checkNull(paramsJson);
    Map<?, ?> paramsMap = JsonUtil.json2Obj(paramsJson, Map.class);
    String type = MyMapUtil.getValueStr(paramsMap, "Type", true);
    UpdateWrapper<SysAdmin> wrapper = new UpdateWrapper<>();
    if ("modify".equals(type)) {
      String passwordOld = MyMapUtil.getValueStr(paramsMap, "PasswordOld", true);
      String passwordNew = MyMapUtil.getValueStr(paramsMap, "PasswordNew", true);
      CommonUtil.checkNull(passwordOld, passwordNew);
      // 先验证旧密码
      if (!isRightPasswordOld(id, passwordOld)) {
        throw new CustomException(ResponseCodeEnum.BAD_PASSWORD);
      }
      // 更新新密码
      wrapper.set("password", passwordEncoder.encode(passwordNew)).eq("id", id);
    } else if ("reset-super".equals(type)) {
      // 重置密码为 xzqwjw
      wrapper.set("password", passwordEncoder.encode("xzqwjw")).eq("id", id);
    } else {
      throw new CustomException(ResponseCodeEnum.BAD_REQUEST);
    }
    if (update(wrapper)) {
      return JsonUtil.obj2Json(ResponseVo.success(ResponseCodeEnum.OK_PATCHED));
    } else {
      throw new CustomException(ResponseCodeEnum.ERROR_PATCH);
    }
  }

  /**
   * 登录成功后更新最后一次登录时间字段
   */
  @Override
  public void patchLastLoginTime(Integer id) {
    CommonUtil.checkId(id);
    UpdateWrapper<SysAdmin> wrapper = new UpdateWrapper<>();
    wrapper.set("dt_last_login", new Date()).eq("id", id);
    if (!update(wrapper)) {
      throw new CustomException(ResponseCodeEnum.ERROR_PATCH);
    }
  }

  // ==================== 查询 ====================

  @Override
  public String queryById(Integer id) {
    CommonUtil.checkId(id);
    SysAdmin sysAdmin = getById(id);
    if (Objects.nonNull(sysAdmin)) {
      return JsonUtil.obj2Json(ResponseVo.success(sysAdmin));
    } else {
      throw new CustomException(ResponseCodeEnum.BAD_REQUEST);
    }
  }

  /**
   * 根据用户名查询管理员
   */
  @Override
  public String queryByUsername(String username) {
    CommonUtil.checkNull(username);
    return JsonUtil.obj2Json(ResponseVo.success(loadUserByUsername(username)));
  }

  /**
   * type 如果值为 admin ，表示列出卫健委所有管理员，查看管理员列表、角色绑定管理员时调用，不列出超管
   * type 如果值为 task ，表示用于任务添加页中显示某任务所属管理员
   */
  @Override
  public String listAll(String type) {
    if ("admin".equals(type)) {
      List<SysAdmin> resultList = adminMapper.listAllByIdUnit(106);
      // resultList.removeIf(sysAdmin -> sysAdmin.getId().equals(9));
      return JsonUtil.obj2Json(ResponseVo.success(resultList));
    } else if ("task".equals(type)) {
      List<Integer> allIdList = categoryService.listAllIdSub();
      QueryWrapper<SysAdmin> wrapper = new QueryWrapper<>();
      wrapper.in("id_department", allIdList);
      return JsonUtil.obj2Json(ResponseVo.success(list(wrapper)));
    } else {
      throw new CustomException(ResponseCodeEnum.BAD_REQUEST);
    }
  }

  /**
   * 管理员在登录时，security验证用户名调用此方法
   */
  @Override
  public UserDetails loadUserByUsername(String username) {
    // 这里如果使用 QueryWrapper 的方式无法得到该管理员的角色和权限
    SysAdmin admin = adminMapper.getAdminByUsername(username);
    if (Objects.isNull(admin)) {
      throw new UsernameNotFoundException(ResponseCodeEnum.BAD_USERNAME.getMessage());
    }
    // 执行到这里说明有此管理员，那么将管理员资料都存好来
    List<SysRole> roleList = admin.getRoleList();
    List<SysPurview> sysPurviewList = admin.getPurviewList();
    // 存放所有角色和权限
    List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
    // 把所有角色赋值给 grantedAuthorities
    for (SysRole r : roleList) {
      grantedAuthorities.add(new SimpleGrantedAuthority(r.getCode()));
    }
    // 把所有权限赋值给grantedAuthorities
    for (SysPurview p : sysPurviewList) {
      grantedAuthorities.add(new SimpleGrantedAuthority(p.getCode()));
    }
    // 设置sysAdmin的authorities属性，注意是既包括了角色又包括了权限的
    admin.setAuthorities(grantedAuthorities);
    // 设置管理员的menu
    admin.setMenu(JsonUtil.obj2Json(generateMenu(admin)));
    return admin;
  }

  // ==================== 私有方法 ====================

  private Map<String, Object> generateMenu(SysAdmin admin) {
    // 如果没有分配角色，则不能得到菜单
    if (CollectionUtils.isEmpty(admin.getRoleList())) {
      return null;
    }
    // 判断是否是超级管理员
    boolean flagSuper = false;
    for (SysRole role : admin.getRoleList()) {
      if (ROLE_NAME_SUPERADMIN.equals(role.getCode())) {
        flagSuper = true;
        break;
      }
    }
    if (flagSuper) {
      // 超级管理员是直接得到所有路由
      return frontRouteService.setMenu(frontRouteService.listAll4Menu(), true);
    } else {
      // 普通管理员先获取拥有权限的路由，再得到权限相对应的map格式菜单
      List<SysPurview> listPurview = admin.getPurviewList();
      if (CollectionUtils.isEmpty(listPurview)) {
        return null;
      }
      List<SysFrontRoute> allRouteList = new ArrayList<>();
      for (SysPurview purview : listPurview) {
        purview = purviewService.getDetailById(purview.getId());
        allRouteList.addAll(purview.getFrontRouteList());
      }
      return frontRouteService.setMenu(allRouteList, false);
    }
  }

  /**
   * 根据管理员用户名查询管理员，判断此管理员是否已添加
   *
   * @param username 管理员用户名
   * @return 是否已添加
   */
  private boolean isAdded(String username) {
    QueryWrapper<SysAdmin> wrapper = new QueryWrapper<>();
    wrapper.select("id").eq("username", username);
    Map<String, Object> map = getMap(wrapper);
    if (MapUtils.isEmpty(map)) {
      return false;
    }
    return ConvertUtil.obj2Int(map.get("id")) > 0;
  }

  /**
   * 根据管理员id和密码验证管理员
   */
  private boolean isRightPasswordOld(int id, String passwordOld) {
    QueryWrapper<SysAdmin> wrapper = new QueryWrapper<>();
    wrapper.select("id")
        .eq("id", id)
        .eq("password", passwordEncoder.encode(passwordOld));
    Map<String, Object> resultMap = getMap(wrapper);
    if (MapUtils.isEmpty(resultMap)) {
      return false;
    }
    return ConvertUtil.obj2Int(resultMap.get("id")) > 0;
  }

}
